{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 基于活动的协同过滤"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#导入相应的模块\n",
    "import pickle\n",
    "import numpy as np\n",
    "import scipy.io as sio\n",
    "import scipy.sparse as ss\n",
    "from numpy.random import random\n",
    "from collections import defaultdict\n",
    "from numpy import linalg as la"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "协同过滤基于之前数据整理实现，先导入之前生成的目标数据文件"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#读取已整理好数据文件\n",
    "userIndex = pickle.load(open(\"PE_userIndex.pkl\",\"rb\"))\n",
    "itemIndex = pickle.load(open(\"PE_eventIndex.pkl\",\"rb\"))\n",
    "n_users = len(userIndex)\n",
    "n_items = len(itemIndex)\n",
    "userItemScores = sio.mmread(\"PE_userEventScores\").todense()\n",
    "itemsForUser = pickle.load(open(\"PE_eventsForUser.pkl\",\"rb\"))\n",
    "usersForItem = pickle.load(open(\"PE_usersForEvent.pkl\",\"rb\"))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "基于活动的协同过滤，先找出对相同的item均有打分的用户，再根据这些用户的打分计算这些item之间的相似度，相似度有很多指标可以表示，比如通过计算欧几里德距离、cosine夹角，相关系数等，由于数据集是稀疏矩阵，直接引用cosine相似度或者相关系数会导致指标的计算过程中分母等于0，所以此处用欧几里德距离倒数的大小来表示相似度的大小，为保证相似度的取值在0，1之间，指标作了修改。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#定义活动之间相似度的函数\n",
    "def simItemBased1(item1, item2):\n",
    "    user_Cmn = []\n",
    "    for user in usersForItem[item1]:\n",
    "        if user in usersForItem[item2]:\n",
    "            user_Cmn.append(user) #得到参与活动1和活动2的所有用户\n",
    "    if len(user_Cmn)==0: \n",
    "        return 0 #如果活动1与活动2没有共同的用户，返回0\n",
    "    score_item1 = np.array([userItemScores[u,item1] for u in user_Cmn]) #参与了活动1的用户对活动1给出的打分\n",
    "    score_item2 = np.array([userItemScores[u,item2] for u in user_Cmn]) #参与了活动2的用户对活动2给出的打分\n",
    "    sim_Between = eclSim(score_item1,score_item2) #根据上述的打分计算这两个活动之间的相似度\n",
    "    return sim_Between #返回相似度\n",
    "def eclSim(A,B): #定义求解相似度的函数\n",
    "    return 1.0/(1.0+la.norm(A-B)) #采用较为简单的欧几里德间距倒数的变形来表示相似度"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "#定义基于活动协同过滤的推荐系统\n",
    "def RecSys1(userId,itemId,Method=simItemBased1): \n",
    "    u = userIndex[userId] #从整理的数据文件中根据用户地址获得用户序号\n",
    "    i = itemIndex[itemId] #从整理的数据文件中根据活动地址获得活动序号\n",
    "    sim_acc = 0 \n",
    "    score_acc = 0\n",
    "    for item in itemsForUser[u]: \n",
    "        sim = Method(item,i) #根据相似度函数获得相似度\n",
    "        sim_acc+=sim\n",
    "        score_acc+=sim*userItemScores[u,item] #根据其它不同活动与该活动之间的相似度对该活动进行预估打分\n",
    "    if score_acc==0:\n",
    "        return 0\n",
    "    ans = score_acc/sim_acc\n",
    "    return ans"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[(1.0, '2116267579'),\n",
       " (1.0, '151502491'),\n",
       " (1.0, '445098029'),\n",
       " (1.0, '1968459057'),\n",
       " (1.0, '2118381839'),\n",
       " (1.0, '2323267944'),\n",
       " (1.0, '1718413929'),\n",
       " (1.0, '348529248'),\n",
       " (1.0, '1718839450'),\n",
       " (1.0, '1421210499'),\n",
       " (1.0, '1554701042'),\n",
       " (1.0, '527915582'),\n",
       " (1.0, '581559015'),\n",
       " (1.0, '3047117217'),\n",
       " (1.0, '199746426'),\n",
       " (1.0, '650745497'),\n",
       " (1.0, '679700922'),\n",
       " (1.0, '3219417278'),\n",
       " (1.0, '2700042611'),\n",
       " (1.0, '4040372452'),\n",
       " (1.0, '2293472224'),\n",
       " (1.0, '2514859165'),\n",
       " (1.0, '2585327580'),\n",
       " (1.0, '316055690'),\n",
       " (1.0, '740269154'),\n",
       " (1.0, '3512048812'),\n",
       " (1.0, '306010070'),\n",
       " (1.0, '1395574268'),\n",
       " (1.0, '3684195312'),\n",
       " (1.0, '61186519'),\n",
       " (0.82842712474619018, '383607907'),\n",
       " (0.75, '881346279'),\n",
       " (0.75, '573886273'),\n",
       " (0.75, '2447943335'),\n",
       " (0.75, '4233837355'),\n",
       " (0.75, '1268088620'),\n",
       " (0.75, '3481603259'),\n",
       " (0.75, '2577118609'),\n",
       " (0.66666666666666663, '1812117472'),\n",
       " (0.66666666666666663, '14945235'),\n",
       " (0.66666666666666663, '3689283674'),\n",
       " (0.66666666666666663, '3315040094'),\n",
       " (0.66666666666666663, '1228296357'),\n",
       " (0.66666666666666663, '3339219031'),\n",
       " (0.66666666666666663, '2168553474'),\n",
       " (0.66666666666666663, '404595428'),\n",
       " (0.66666666666666663, '2668074570'),\n",
       " (0.66429079605187491, '4125420656'),\n",
       " (0.625, '2398192028'),\n",
       " (0.62361503263076601, '966971643'),\n",
       " (0.60411540967461619, '152418051'),\n",
       " (0.59999999999999998, '794450376'),\n",
       " (0.55555555555555558, '739161736'),\n",
       " (0.55555555555555558, '799782433'),\n",
       " (0.54750494509935776, '4203627753'),\n",
       " (0.5, '1528500344'),\n",
       " (0.5, '2635162962'),\n",
       " (0.5, '949519938'),\n",
       " (0.5, '2682064547'),\n",
       " (0.5, '929107951')]"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "Res_s = [] #定义空列表\n",
    "for itemId in itemIndex: \n",
    "    res = RecSys1(\"4236494\",itemId) #同其他两个协同过滤一样，随机指定一个用户，如\"4236494\"，对其进行活动推荐\n",
    "    Res_s.append((res, itemId)) #\n",
    "sorted(Res_s, key=lambda x: x[0],reverse=True)[:60] #将推荐的活动按照预估打分值进行排序，排在前面表示用户可能更感兴趣"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "从推荐结果来看，对随机指定的同一个用户，svd模型协同过滤推荐的活动排序与其他两个的推荐并不相同。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
